import audio from '@ohos.multimedia.audio';
import common from '@ohos.app.ability.common';
import { WaveType } from '../models/WaveType';

const TAG = 'AudioRendererDemo';
const AMPLITUDE = 0.5
const SAMPLE_RATE = 44100

export class AudioRendererPlayer {
  frequency: number
  wavType = WaveType.SINE
  t: number = 0
  private isRunning = true
  private context: common.Context
  private renderModel: audio.AudioRenderer
  private audioStreamInfo = {
    samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
    channels: audio.AudioChannel.CHANNEL_2, // 通道
    sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
    encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
  }
  private audioRendererInfo = {
    content: audio.ContentType.CONTENT_TYPE_MUSIC, // 媒体类型
    usage: audio.StreamUsage.STREAM_USAGE_MEDIA, // 音频流使用类型
    rendererFlags: 0 // 音频渲染器标志
  }
  private audioRendererOptions = {
    streamInfo: this.audioStreamInfo,
    rendererInfo: this.audioRendererInfo
  }

  constructor(context: common.Context) {
    this.context = context
  }

  // 初始化，创建实例，设置监听事件
  async init() {
    await audio.createAudioRenderer(this.audioRendererOptions).then((renderer) => {
      console.info(`${TAG}: creating AudioRenderer success`);
      this.renderModel = renderer;
      this.renderModel.on('stateChange', (state) => { // 设置监听事件，当转换到指定的状态时触发回调
        if (state == 2) {
          console.info('audio renderer state is: STATE_RUNNING');
        }
      });
      this.renderModel.on('markReach', 1000, (position) => { // 订阅markReach事件，当渲染的帧数达到1000帧时触发回调
        if (position == 1000) {
          console.info('ON Triggered successfully');
        }
      });
    }).catch((reason) => {
      console.info(`${TAG}: creating AudioRenderer failed, error: ${reason}`);
    })
  }

  async play() {
    this.isRunning = true

    // 开始一次音频渲染
    let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];
    if (stateGroup.indexOf(this.renderModel.state) === -1) { // 当且仅当状态为prepared、paused和stopped之一时才能启动渲染
      console.error(TAG + 'start failed');
      return;
    }
    await this.renderModel.start(); // 启动渲染

    const bufferSize = 800;
    const data = new Int16Array(bufferSize)

    const dt = 1.0 / SAMPLE_RATE;

    while(true) {
      if (!this.isRunning) {
        await this.renderModel.drain()
        await this.renderModel.stop()
        break
      }

      // buf是要写入缓冲区的音频数据，在调用AudioRenderer.write()方法前可以进行音频数据的预处理，实现个性化的音频播放功能，AudioRenderer会读出写入缓冲区的音频数据进行渲染
      for (let i = 0; i < bufferSize; i++) {
        data[i] = this.createWav()
        this.t += dt;
        if (this.t >= 1.0 / this.frequency) {
          this.t -= 1.0 / this.frequency;
        }
      }

      let writeSize = await new Promise((resolve, reject) => {
        this.renderModel.write(data.buffer, (err, writeSize) => {
          if (err) {
            reject(err);
          } else {
            resolve(writeSize);
          }
        });
      });
      if (this.renderModel.state === audio.AudioState.STATE_RELEASED) { // 如果渲染器状态为released，停止渲染
        await this.renderModel.stop();
      }
    }
  }

  private createWav(): number {
    switch (this.wavType) {
      case WaveType.SINE: {
        return AMPLITUDE * (32767.0 * Math.sin(2.0 * Math.PI * this.frequency * this.t))
      }
      case WaveType.SQUARE: {
        const wave = (this.t < 0.5 / this.frequency) ? AMPLITUDE * 32767 : -AMPLITUDE * 32767
        return wave * 0.3
      }
      case WaveType.TRIANGLE: {
        const dividend = this.t * this.frequency
        const divisor = 1.0
        const position = ((dividend % divisor) + divisor) % divisor

        // Determine the triangle wave value based on the position
        let wave: number
        if (position < 0.25) {
            wave = AMPLITUDE * 32767 * (4 * position);
        } else if (position < 0.75) {
            wave = AMPLITUDE * 32767 * (2 - 4 * position);
        } else {
            wave = AMPLITUDE * 32767 * (4 * position - 4);
        }
        return wave
      }
      case WaveType.SAWTOOTH: {
        const dividend = this.t * this.frequency
        const divisor = 1.0
        const position = ((dividend % divisor) + divisor) % divisor

        const wave = AMPLITUDE * 32767 * (2 * position - 1);
        return wave * 0.5
      }
    }
  }

  async stop() {
    this.isRunning = false
  }
}